﻿namespace System.ComponentModel.DataAnnotations
{

    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
    public class UnsignedNumberAttribute : BaseValidationAttribute
    {
        public UnsignedNumberAttribute()
        {
            this.ErrorMessageCode = "{0}不能为空";
        }
        /// <summary> 浮点类型精度 </summary>
        public int Decimals { get; set; } = -1;

        /// <summary> 是否可为空 </summary>
        public bool AllowNullable { get; set; } = false;

        public UnsignedNumberAttribute(int decimals) : this(decimals, false)
        { }

        public UnsignedNumberAttribute(bool allowNullable) : this(-1, allowNullable)
        { }

        public UnsignedNumberAttribute(int decimals = -1, bool allowNullable = false)
        {
            Decimals = decimals;
            AllowNullable = allowNullable;
            this.ErrorMessageCode = "{0}不能为空";
        }

        public override bool IsValid(object value)
        {
            if (value == null) return AllowNullable;

            if (value is int intValue) return (intValue > 0) || (AllowNullable && intValue == default(int));

            if (value is uint uintValue) return (uintValue > 0) || (AllowNullable && uintValue == default(uint));

            if (value is long longValue) return (longValue > 0) || (AllowNullable && longValue == default(long));

            if (value is decimal decimalValue)
            {
                if (decimalValue < 0 || (!AllowNullable && decimalValue == default(decimal)))
                {
                    return false;
                }
                return CheckDecimalDecimals(decimalValue);
            }

            if (value is IEnumerable<int> intList) return !intList.Any(a => a <= 0) && CheckAllowNullable(intList);

            if (value is IEnumerable<uint> uintList) return !uintList.Any(a => a <= 0) && CheckAllowNullable(uintList);

            if (value is IEnumerable<long> longList) return !longList.Any(a => a <= 0) && CheckAllowNullable(longList);

            if (value is IEnumerable<decimal> decimalList)
            {
                if (decimalList.Any(a => a <= 0)) return false;
                if (!CheckAllowNullable(decimalList)) return false;
                foreach (var item in decimalList)
                {
                    if (!CheckDecimalDecimals(item)) return false;
                }
                return true;
            }

            throw new Exception($"{nameof(UnsignedNumberAttribute)}不支持数据类型{value.GetType().FullName}");
        }

        private bool CheckDecimalDecimals(decimal value)
        {
            if (Decimals < 0) return true;
            var strDecimal = value.TrimEndZero();
            var index = strDecimal.IndexOf(".");
            if (index < 0) return true;
            return (strDecimal.Length - index - 1) <= Decimals;
        }

        private bool CheckAllowNullable<T>(IEnumerable<T> list)
        {
            return AllowNullable || list.Any();
        }
    }
}
